package pt.iscte.dcti.pa.model;

import java.util.List;
import java.util.Observable;
import java.util.Observer;

import utils.Common;
import utils.SingletonRmiWrapper;

public class Cell extends Observable implements Observer {

	private int abscissa; // XX
	private int ordinate; // YY
	private String value = null;
	private WorksheetModel model;

	public Cell(int abscissa, int ordinate, WorksheetModel _model) {
		this.abscissa = abscissa;
		this.ordinate = ordinate;
		this.model = _model;
	}

	public int getAbscissa() {
		return abscissa;
	}

	public int getOrdinate() {
		return ordinate;
	}

	public String getValue() {

		return this.value;
	}

	public void setValue(String value) {

		this.value = value;
	
	}

	public void setObserver(Observer obs) {
		this.addObserver(obs);
	}

	@Override
	public void update(Observable o, Object arg) {

		Cell observableCell = (Cell) o;
		
//		System.out.println(String.format("A celula [%s;%s] mandou a celula [%s;%s] actulizar o ser valor para %s", 
//				observableCell.getAbscissa(), 
//				observableCell.getOrdinate(), 
//				this.getAbscissa(), 
//				this.getOrdinate()));	//Nao precisa do valor porque vai busca-lo novamente
		String updatedValue = this.doMagik(false);
	}

	public String doMagik(boolean propagateValue) {
		String newValue = this.value;

		if (newValue.charAt(0) == '=') {

			newValue = newValue.substring(1); // retira o "="

			List<String> operands = Common.getMatches("[A-Z]+\\d+", newValue);

			int[] coordinates;
			String cellResult = null;
			for (String operand : operands) {
				coordinates = Common.GetCoordinates(operand);

				try {

					cellResult = computeCellValue(coordinates);

				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				newValue = newValue.replace(operand, cellResult);
			}

		}
		
		Mathematician mathematician = new Mathematician(newValue);
		newValue = mathematician.doMath();
		
		if (propagateValue) {
			SingletonRmiWrapper rmiWrap = SingletonRmiWrapper.getWrapper();
			if (rmiWrap != null) {
				rmiWrap.updateColaboratorsCell(newValue);
			}
		}
		
		
		//TODO Debug
		//Actualiza a View (pt.iscte.dcti.pa.view.VisibleElement)
		//Actualiza o Model (pt.iscte.dcti.pa.model.Cell) *Celulas referentes
		setChanged();		
		notifyObservers(newValue);

		return newValue;
	}

	private String computeCellValue() {
		Cell targetCell = model.getCell(abscissa, ordinate);
		targetCell.addObserver(this);
		String cellValue = targetCell.getValue();

		// se for um numero
		if (Common.isDigit(cellValue)) {
			return cellValue;
		}

		List<String> operands = Common.getMatches("[A-Z]+\\d+", cellValue);
		String auxCellValue;
		for (String operand : operands) {

			try {

				auxCellValue = computeCellValue(Common.GetCoordinates(operand));
				cellValue = cellValue.replace(operand, auxCellValue);

			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
		
		Mathematician mathematician = new Mathematician(cellValue);
		return mathematician.doMath();
	}

	private String computeCellValue(int[] coordinates) throws Exception {
		Cell targetCell = model.getCell(coordinates[0], coordinates[1]);
		targetCell.addObserver(this);
		String cellValue = targetCell.getValue();

		// se for um numero
		if (Common.isDigit(cellValue)) {
			return cellValue;
		}

		List<String> operands = Common.getMatches("[A-Z]+\\d+", cellValue);
		String auxCellValue;
		for (String operand : operands) {
			auxCellValue = computeCellValue(Common.GetCoordinates(operand));
			cellValue = cellValue.replace(operand, auxCellValue);
		}
		
		Mathematician mathematician = new Mathematician(cellValue);
		return mathematician.doMath();
	}
	
	@Override
	public String toString() {
		return String.format("Abscissa: %s; Ordinate: %s; Value: %s",
				this.getAbscissa(), this.getOrdinate(), this.getValue());
	}
}
